package com.wg.net.peer;

import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.wg.game.app.BaseApplication;
import com.wg.net.DisconnectReason;
import com.wg.net.*;
import com.wg.net.operations.OperationCode;
import com.wg.net.operations.PingRequest;
import com.wg.net.operations.base.OperationRequest;
import com.wg.net.operations.base.OperationResponse;
import com.wg.net.operations.base.S2SOperationRequest;
import com.wg.net.protocols.BinaryByteProtocol;
import com.wg.net.protocols.MessageHeader;
import com.wg.net.session.ISession;
import com.wg.net.client.ServerToServerClient;
import com.wg.net.client.tcp.operation.InitRequest;

import java.net.InetSocketAddress;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 服务器到服务器连接抽象基类（处理S2S连接的建立、维护与断开）
 * <p>
 * 职责：
 * - 管理外发连接的生命周期（连接、重连、断开）
 * - 实现心跳检测与连接保活
 * - 处理连接状态转换与事件回调
 * - 提供基础网络操作（如发送初始化请求、Ping请求）
 *
 * @author 少爷123
 */
public abstract class AbstractOutboundServerToServerPeer extends BaseServerToServerPeer {
    private static final Log log = LogFactory.get();

    /**
     * 心跳间隔（毫秒）
     */
    private static final int HEARTBEAT_INTERVAL_MS = 2000;
    /**
     * 重连间隔（毫秒，初始值）
     */
    private static final int RECONNECT_INITIAL_DELAY_MS = 2000;
    /**
     * 最大重连间隔（毫秒，指数退避上限）
     */
    private static final int MAX_RECONNECT_DELAY_MS = 30000;
    /**
     * 未管理会话（用于连接建立成功时临时保存）
     */
    private static final DisconnectedSession DISCONNECTED_PEER = new DisconnectedSession();
    /**
     * 应用ID（标识当前服务）
     */
    private final String appId;
    /**
     * 客户端（封装TCP连接逻辑）
     */
    private final ServerToServerClient client;
    /**
     * 目标服务器地址
     */
    private volatile InetSocketAddress socketAddress;
    /**
     * 重连调度任务
     */
    private final AtomicReference<ScheduledFuture<?>> reconnectSchedulerRef = new AtomicReference<>(null);
    /**
     * 心跳调度任务
     */
    private final AtomicReference<ScheduledFuture<?>> heartbeatSchedulerRef = new AtomicReference<>(null);
    /**
     * 重连尝试次数（用于指数退避策略）
     */
    private final AtomicInteger reconnectAttempts = new AtomicInteger(0);

    /**
     * 构造服务器对端（初始化连接参数）
     *
     * @param appId               应用ID（标识当前服务）
     * @param connectionTimeoutMs 连接超时时间（毫秒）
     * @param idleTimeoutMs       空闲超时时间（毫秒）
     */
    protected AbstractOutboundServerToServerPeer(String appId, int connectionTimeoutMs, int idleTimeoutMs) {
        super(BinaryByteProtocol.getInstance(), DISCONNECTED_PEER, ConnectionState.Disconnected);
        this.appId = appId;
        this.disposeOnDisconnect = false;
        this.client = new ServerToServerClient(connectionTimeoutMs, idleTimeoutMs, this);
    }

    /**
     * 连接（异步）
     *
     * @param ip   目标服务器IP
     * @param port 目标服务器端口
     * @throws IllegalArgumentException 若IP或端口无效
     */
    public void connect(String ip, int port) {
        validateConnectParams(ip, port);
        this.socketAddress = new InetSocketAddress(ip, port);
        doConnect();
    }

    /**
     * 关闭当前连接（释放资源）
     */
    @Override
    public void close() {
        super.close();
        cancelSchedulers(); // 取消重连和心跳任务
        setUnmanagedSession(DISCONNECTED_PEER); // 重置未管理会话
    }

    /**
     * 释放所有资源（包括客户端和调度任务）
     */
    @Override
    public void dispose() {
        super.dispose();
        cancelSchedulers(); // 取消重连和心跳任务
        setUnmanagedSession(DISCONNECTED_PEER); // 重置未管理会话
        client.dispose();
    }

    /**
     * 初始化连接状态（线程安全）
     *
     * @return 初始化是否成功
     */
    private boolean initializeConnect() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (connectionState == ConnectionState.Disposed) {
                log.error("连接已释放，无法初始化");
                return false;
            }
            if (connectionState != ConnectionState.Disconnected) {
                log.warn("连接已处于非断开状态（当前状态：{}），无需重复初始化", connectionState);
                return false;
            }
            connectionState = ConnectionState.Connecting;
            return true;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 连接建立成功回调（抽象方法，子类实现具体逻辑）
     *
     * @param responseObject 连接建立响应对象
     */
    protected abstract void onConnectionEstablished(Object responseObject);

    /**
     * 连接失败回调（抽象方法，子类实现具体逻辑）
     *
     * @param errorCode    错误编号（协议定义）
     * @param errorMessage 错误描述（具体原因）
     */
    protected abstract void onConnectionFailed(int errorCode, String errorMessage);

    /**
     * 处理连接建立完成（内部逻辑）
     *
     * @param session 已连接的未管理会话
     */
    private void handleConnectionEstablished(ISession session) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            connectionState = ConnectionState.Connected;
        } finally {
            lock.unlock();
        }

        log.info("外发连接建立成功 | 目标地址={}:{}, 本地地址={}:{}, 连接ID={}",
                getRemoteIp(), getRemotePort(), getLocalIp(), getLocalPort(), getConnectionId());
        setUnmanagedSession(session);
        reconnectAttempts.set(0);
        sendInitRequest();
    }

    /**
     * 处理连接失败（内部逻辑）
     *
     * @param errorCode    错误编号
     * @param errorMessage 错误描述
     */
    private void handleConnectionFailed(int errorCode, String errorMessage) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            connectionState = ConnectionState.Disconnected;
        } finally {
            lock.unlock();
        }
        log.error("连接失败 | 目标地址={}:{}, 错误码={}, 错误信息={}", getRemoteIp(), getRemotePort(), errorCode, errorMessage);
        onConnectionFailed(errorCode, errorMessage); // 触发业务层回调
        setUnmanagedSession(DISCONNECTED_PEER); // 重置未管理会话
        scheduleReconnect(); // 启动重连任务
    }

    /**
     * 发送初始化请求（内部逻辑）
     */
    private void sendInitRequest() {
        InitRequest initRequest = new InitRequest();
        initRequest.setAppId(appId);

        OperationRequest request = new OperationRequest(OperationCode.INIT_REQUEST, initRequest);
        byte[] initData = protocol.serializeInitRequest(request);
        if (initData == null) {
            log.error("初始化请求序列化失败");
            return;
        }

        SendResult sendResult = getUnmanagedSession().send(initData);
        if (sendResult != SendResult.Ok) {
            log.error("初始化请求发送失败 | 结果={}", sendResult);
        }
    }

    /**
     * 执行连接操作（内部逻辑）
     */
    private void doConnect() {
        if (socketAddress == null) {
            log.warn("连接地址为空，跳过连接");
            return;
        }

        if (!initializeConnect()) {
            return;
        }

        try {
            client.connect(socketAddress.getHostString(), socketAddress.getPort());
        } catch (Exception e) {
            log.error("连接建立异常 | 目标地址={}:{}, 异常信息={}", getRemoteIp(), getRemotePort(), e.getMessage(), e);
            onOutboundConnectionFailed(-1, e.getMessage());
        }
    }

    /**
     * 启动重连任务（指数退避策略）
     */
    private void scheduleReconnect() {
        if (connectionState == ConnectionState.Disposed) {
            log.debug("已释放，跳过重连");
            return;
        }

        // 指数退避计算（初始延迟2s，最大30s）
        int attempts = reconnectAttempts.getAndIncrement();
        // 限制 attempts，避免位移溢出
        int capped = Math.min(attempts, 30);
        long delay = RECONNECT_INITIAL_DELAY_MS;
        if (capped > 0) {
            long shifted = ((long) RECONNECT_INITIAL_DELAY_MS) << capped;
            delay = Math.min(shifted, (long) MAX_RECONNECT_DELAY_MS);
        }

        // 取消旧任务（若存在），并替换为新任务
        ScheduledFuture<?> old = reconnectSchedulerRef.getAndSet(
                GlobalServerTimer.INSTANCE.schedule(() -> {
                    log.info("尝试重连 | 目标地址={}:{}, 重试次数={}", getRemoteIp(), getRemotePort(), reconnectAttempts);
                    doConnect();
                }, delay, TimeUnit.MILLISECONDS));
        if (old != null) {
            old.cancel(false);
        }
    }

    /**
     * 启动心跳检测任务
     */
    private void scheduleHeartbeat() {
        if (connectionState != ConnectionState.Connected) {
            log.debug("非已连接状态，跳过心跳启动");
            return;
        }

        ScheduledFuture<?> old = heartbeatSchedulerRef.getAndSet(
                GlobalServerTimer.INSTANCE.scheduleAtFixedRate(() -> {
                    try {
                        sendPing();
                    } catch (Throwable t) {
                        log.error("心跳任务异常", t);
                    }
                }, HEARTBEAT_INTERVAL_MS, HEARTBEAT_INTERVAL_MS, TimeUnit.MILLISECONDS));
        if (old != null)
            old.cancel(false);
    }

    /**
     * 发送Ping请求（连接保活）
     */
    private void sendPing() {
        if (connectionState != ConnectionState.Connected) {
            log.warn("当前状态非连接，跳过Ping发送");
            return;
        }

        PingRequest pingRequest = new PingRequest(System.currentTimeMillis());
        S2SOperationRequest operationRequest = new S2SOperationRequest(OperationCode.PING, pingRequest);

        try {
            sendInternalOperationRequest(operationRequest);
        } catch (Exception e) {
            log.error("Ping请求发送失败 | 异常信息={}", e.getMessage(), e);
        }
    }

    /**
     * 处理外发连接建立完成事件（网络层回调）
     *
     * @param session 已连接的未管理会话
     */
    public void onOutboundConnectionEstablished(ISession session) {
        requestFiber.enqueue(() -> handleConnectionEstablished(session));
    }

    /**
     * 处理外发连接失败事件（网络层回调）
     *
     * @param errorCode    错误编号
     * @param errorMessage 错误描述
     */
    public void onOutboundConnectionFailed(int errorCode, String errorMessage) {
        requestFiber.enqueue(() -> handleConnectionFailed(errorCode, errorMessage));
    }

    /**
     * 处理接收的内部消息（网络层回调）
     *
     * @param data 原始字节数据
     */
    @Override
    void onReceiveInternal(byte[] data) {
        if (connectionState != ConnectionState.Connecting) {
            super.onReceiveInternal(data);
            return;
        }

        MessageHeader header = protocol.parseMessageHeader(data);
        if (header == null) {
            log.warn("无效的S2S消息头，断开连接 | 数据长度={}", data.length);
            close();
            return;
        }

        OperationResponse operationResponse = protocol.parseOperationResponse(data, header.isZip());
        if (operationResponse == null || operationResponse.getOpCode() != OperationCode.INIT_REQUEST) {
            log.warn("非预期的消息类型 | 消息类型={}", header.getMessageType());
            close();
            return;
        }

        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            connectionState = ConnectionState.Connected;
        } finally {
            lock.unlock();
        }

        onConnectionEstablished(operationResponse); // 触发连接建立成功回调
        scheduleHeartbeat(); // 启动心跳检测
    }

    /**
     * 应用层断开连接回调（覆盖父类实现）
     *
     * @param reasonCode   断开原因代码
     * @param reasonDetail 断开详细说明
     * @return 是否允许断开（当前实现始终允许）
     */
    @Override
    public boolean applicationOnDisconnect(DisconnectReason reasonCode, String reasonDetail) {
        super.applicationOnDisconnect(reasonCode, reasonDetail);
        return false;
    }

    /**
     * 应用层断开连接事件（网络层回调）
     *
     * @param reasonCode   断开原因代码
     * @param reasonDetail 断开详细说明
     */
    @Override
    void onApplicationDisconnect(DisconnectReason reasonCode, String reasonDetail) {
        super.onApplicationDisconnect(reasonCode, reasonDetail);
        setUnmanagedSession(DISCONNECTED_PEER);
        if (BaseApplication.INST.isRunning()) {
            scheduleReconnect(); // 应用运行中时自动重连
        }
    }

    // ------------------------------ 私有工具方法 ------------------------------

    /**
     * 验证连接参数有效性
     *
     * @param ip   目标IP
     * @param port 目标端口
     * @throws IllegalArgumentException 若参数无效
     */
    private void validateConnectParams(String ip, int port) {
        if (StrUtil.isBlank(ip)) {
            throw new IllegalArgumentException("目标IP不能为空");
        }
        if (port <= 0 || port > 65535) {
            throw new IllegalArgumentException("目标端口无效（范围：1-65535）");
        }
    }

    /**
     * 取消所有调度任务（重连和心跳）
     */
    private void cancelSchedulers() {
        ScheduledFuture<?> s1 = reconnectSchedulerRef.getAndSet(null);
        if (s1 != null) {
            s1.cancel(false);
            log.debug("重连任务已取消");
        }
        ScheduledFuture<?> s2 = heartbeatSchedulerRef.getAndSet(null);
        if (s2 != null) {
            s2.cancel(false);
            log.debug("心跳任务已取消");
        }
    }

    // ------------------------------ 内部类与常量 ------------------------------

    /**
     * 已断开连接的会话占位符（避免空指针）
     */
    private static class DisconnectedSession implements ISession {
        @Override
        public long getConnectionId() {
            return -1;
        }

        @Override
        public String getPeerId() {
            return "";
        }

        @Override
        public SendResult send(byte[] data) {
            return SendResult.Disconnected;
        }

        @Override
        public void flush() {
        }

        @Override
        public void close() {
        }

        @Override
        public void disconnect(DisconnectReason reasonCode) {
        }

        @Override
        public int getLocalPort() {
            return 0;
        }

        @Override
        public String getLocalIp() {
            return "";
        }

        @Override
        public int getRemotePort() {
            return 0;
        }

        @Override
        public String getRemoteIp() {
            return "";
        }

        @Override
        public Object getUserData() {
            return null;
        }

        @Override
        public void setUserData(Object pObj) {
        }
    }
}